import time
import unittest
import uuid
import warnings
from base.runmethod import RunMethod
from base.base_config import BASE_URL, DB_MD
from common.check import check_response_status
from common.logger import Logger
from common.devices_sign import regist_sign, login_sign
from configs import config_manage


class BaseCase(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        warnings.simplefilter('ignore', ResourceWarning)
        # 启动测试方法
        cls.run_method = RunMethod()
        pass

    @classmethod
    def tearDownClass(cls) -> None:
        pass

    def setUp(self) -> None:
        self.log = Logger(custom_color=True, name='base_case')
        self.log.info("--------------------【{}测试用例开始】--------------------".format(self.__class__.__name__))

    def tearDown(self) -> None:
        self.log.info("--------------------【{}测试用例结束】--------------------\n".format(self.__class__.__name__))
        self.log.del_handler()  # 防止重复写入日志

    def base_test(self, i, data_case, requestId=None):
        """
        基础测试框
        :param i: 用例行
        :param data_case: 测试用例
        :return:
        """
        self._testMethodDoc = data_case.get_title(i)  # 传入用例标题
        self.log.info("用例标题: " + self.shortDescription()) # 打印日志信息

        is_run = data_case.get_is_run(i)  # 是否运行，测试用例中用yes则代表需要执行该用例
        if is_run:

            url = BASE_URL + data_case.get_request_url(i)  # 用例url
            method = data_case.get_request_method(i)  # 请求方法
            request_data = data_case.get_request_data(i)  # 请求数据
            expect = data_case.get_expect_data(i)  # 预期结果
            header = data_case.is_header(i)  # 请求头
            depend = data_case.is_depend(i)  # 依赖
            depend_data = data_case.get_depend_data(i)  # 依赖请求数据
            depend_key = data_case.get_depend_key(i)  # 依赖关键字参数
            sql_data = data_case.get_sql(i)  # 获取sql

            header["Authorization"] = config_manage.read_token()["Data"]["Token"]  # 更新token到请求头中

            # 执行查询数据库逻辑
            if sql_data is not None:
                for data in sql_data:
                    sql = data["sql"]  # 获取sql
                    result = DB_MD.ExecQuery(sql)

                    if result:
                        result = str(result[0][0])  # 第一行第一个字段值
                    else:
                        raise ValueError("查询结果为空，请检查sql或数据")

                    # 根据key赋值到请求数据/依赖请求数据/预期结果
                    for key in data.keys():
                        if key == "request":
                            for_request = data[key]
                            request_data[for_request] = result
                        elif key == "expect":
                            for_expect = data[key]
                            expect[for_expect] = result
                        elif key == "depend":
                            for_depend = data[key]
                            depend_data[for_depend] = result
                        elif key == "sql":
                            pass
                        else:
                            raise ValueError(self.log.info("键应为 1.request 2.expect 3.depend，请检查键是否正确"))

            # 执行依赖接口逻辑
            if depend is not None:
                depend = eval(depend)  # str转字典
                depend_url = BASE_URL + depend["url"]  # 依赖接口
                depend_method = depend["method"]  # 请求方式
                depend_content = depend["content"]  # 接口描述

                ## 依赖接口->测试前置条件，发送请求，获取响应结果
                res = RunMethod().run_main(method=depend_method, url=depend_url, data=depend_data,
                                           header=header)
                data_case.write_result_depend(i, res.text)
                self.log.info("**********执行依赖：{}**********".format(depend_content))
                self.log.info("请求接口: " + depend_url)
                self.log.info("请求头:" + str(header))
                self.log.info("请求数据:" + str(depend_data))
                self.log.info("返回响应: " + res.text)
                check_response_status(res, isprint=False)  # 检查执行是否成功

                # 更新依赖值到请求数据中
                if depend_key is not None:
                    for key, value in depend_key.items():
                        res_value = self.get_keyValue(res=res.json(), key=key)
                        request_data[value] = res_value

            # 更新UUID到requestID
            if requestId is not None:
                request_data["RequestID"] = uuid.uuid1()

            self.log.info("**********执行主用例**********")
            res = RunMethod().run_main(method=method, url=url, data=request_data, header=header)

            self.base_assert(i, url, header, request_data, expect, res, data_case)  # 断言

    def regist_and_login(self, i, data_case, key_ts=None, key_regist=None, key_login=None, key_header=None):
        """
        设备注册和登录接口用
        :param i:excle表单的row num
        :param data_case:exlce中sheet的整页数据集合
        :param key_ts: 时间戳
        :param key_regist: 注册标识
        :param key_login: 登录标识
        :param key_header: 请求头带验签标识
        :return:
        """
        self._testMethodDoc = data_case.get_title(i)  # 传入用例标题
        self.log.info("用例标题: " + self.shortDescription())  # shortDescription() 返回一行描述的测试结果信息

        is_run = data_case.get_is_run(i)  # 用例是否运行字段值为yes则返回True
        if is_run:
            url = BASE_URL + data_case.get_request_url(i)  # 用例url
            method = data_case.get_request_method(i)  # 请求方法
            request_data = data_case.get_request_data(i)  # 请求数据
            expect = data_case.get_expect_data(i)  # 预期结果
            header = data_case.is_header(i)  # 请求头
            depend = data_case.is_depend(i)  # 依赖
            depend_data = data_case.get_depend_data(i)  # 依赖请求数据
            depend_key = data_case.get_depend_key(i)  # 依赖关键字参数
            # sql_data = data_case.get_sql(i)  # 获取sql

            # 时间戳
            ts = int(time.time() * 1000)  # 毫秒
            if key_ts:
                request_data[key_ts] = ts

            # 当存在key_login,注册接口放到到依赖逻辑
            if key_login:
                if depend is not None:
                    depend = eval(depend)  # 转字典 depend_url
                    depend_url = BASE_URL + depend["url"]  # 依赖接口
                    depend_method = depend["method"]  # 请求方式
                    depend_content = depend["content"]  # 接口描述

                    # 登录接口引用注册接口时用
                    if key_regist:
                        # regist_sign 原开发人员所属产品的代码jar包调用，群艺使用的话基本没用
                        sign = regist_sign(macid=depend_data["MacID"], terminaltype=depend_data["TerminalType"],
                                           key=depend_data["Sign"], ts=ts)
                        depend_data[key_regist] = sign
                        depend_data[key_ts] = ts
                        if key_header:
                            header[key_header] = sign

                    res = RunMethod().run_main(method=depend_method, url=depend_url, data=depend_data,
                                               header=header)
                    data_case.write_result_depend(i, res.text)  # 把依赖接口响应结果写入excle的depend_result列下单元格
                    self.log.info("**********执行依赖：{}**********".format(depend_content))
                    self.log.info("请求接口: " + depend_url)
                    self.log.info("返回响应: " + res.text)
                    check_response_status(res, isprint=False)  # 检查执行是否成功

                    # 根据关键字获取返回值赋予主用例对应的键值
                    if depend_key is not None:
                        for key, value in depend_key.items():
                            res_value = self.get_keyValue(res=res.json(), key=key)
                            request_data[value] = res_value

                    # 获取登录验签
                    appid = self.get_keyValue(res=res.json(), key="AppID")
                    appkey = self.get_keyValue(res=res.json(), key="AppKey")
                    loginsign = login_sign(terminaltype=request_data["TerminalType"], macid=request_data["MacID"], ts=ts, appid=appid, appkey=appkey,
                                           appver=request_data["AppVer"])  # 获取登录验签
                    request_data["Sign"] = loginsign
                    # request_data.pop("Authorization")

                    self.log.info("执行主用例".center(20, "*"))
                    res = RunMethod().run_main(method=method, url=url, data=request_data, header=header)

                    is_success = self.base_assert(i, url, header, request_data, expect, res, data_case)
                    # 写入token到token.json文件
                    if is_success is True:
                        config_manage.write_token(res.json())

            else:
                if key_regist:
                    sign = regist_sign(macid=request_data["MacID"], terminaltype=request_data["TerminalType"],
                                       key=request_data["Sign"], ts=ts)
                    request_data[key_regist] = sign
                    if key_header:
                        header[key_header] = sign

                self.log.info("执行主用例".center(20, "*"))
                res = RunMethod().run_main(method=method, url=url, data=request_data, header=header)

                self.base_assert(i, url, header, request_data, expect, res, data_case)

    def base_assert(self, i, url, header, request_data, expect, res, data_case):
        """
        通用断言，根据预期结果键值对>断言>实际结果，相等则成功，反之失败
        :param i: 用例行
        :param url: 用例接口
        :param header: 请求头
        :param request_data: 请求数据
        :param expect: 预期结果
        :param res: 实际结果
        :param data_case: 用例
        :return: 是否成功标识
        """
        expect_keys = list(expect.keys())
        len_keys = len(expect_keys)

        global isSuccess

        for index, key in enumerate(expect_keys):
            # 最后键标识位
            if len_keys == index + 1:
                is_last = True
            else:
                is_last = False

            # 根据预期结果的键查询响应结果对应键，并断言
            if key in res.json().keys():
                e = type(res.json()[key])(expect[key])  # 转换expect值的type为result值的type
                try:
                    self.assertEqual(e, res.json()[key])
                except AssertionError:
                    self.log.info("请求接口: " + url)
                    self.log.info("请求头: " + str(header))
                    self.log.info("请求数据: " + str(request_data))
                    self.log.info("实际结果: " + res.text)
                    self.log.info("预期结果: " + str(expect))
                    data_case.write_result(i, res.text)
                    data_case.write_is_success(i, "Fail")
                    isSuccess = False
                    raise AssertionError(self.log.error("断言失败"))
                else:
                    if is_last is True:
                        self.log.info("请求接口: " + url)
                        self.log.info("请求头: " + str(header))
                        self.log.info("请求数据: " + str(request_data))
                        self.log.info("预期结果：" + str(expect))
                        self.log.info("实际结果：" + res.text)
                        self.log.info("断言成功")
                        data_case.write_result(i, res.text)
                        data_case.write_is_success(i, "Success")
                        isSuccess = True
            else:
                for k, v in res.json().items():
                    v_type = type(v)
                    while v_type != dict:
                        if v_type is list:
                            v_list = v
                            for vv in v_list:
                                if type(vv) is dict:
                                    v = vv
                                    v_type = dict
                        else:
                            break
                    else:
                        if key in v.keys():
                            e = type(v[key])(expect[key])  # 转换expect值的type为result值的type
                            try:
                                self.assertEqual(e, v[key])
                            except AssertionError:
                                self.log.info("请求接口: " + url)
                                self.log.info("请求头: " + str(header))
                                self.log.info("请求数据: " + str(request_data))
                                self.log.info("实际结果: " + res.text)
                                self.log.info("预期结果: " + str(expect))
                                data_case.write_result(i, res.text)
                                data_case.write_is_success(i, "Fail")
                                isSuccess = False
                                raise AssertionError(self.log.error("断言失败"))
                            else:
                                if is_last is True:
                                    self.log.info("请求接口: " + url)
                                    self.log.info("请求头: " + str(header))
                                    self.log.info("请求数据: " + str(request_data))
                                    self.log.info("预期结果：" + str(expect))
                                    self.log.info("实际结果：" + res.text)
                                    self.log.info("断言成功")
                                    data_case.write_result(i, res.text)
                                    data_case.write_is_success(i, "Success")
                                    isSuccess = True
        return isSuccess

    @staticmethod
    def get_keyValue(res, key):
        """
        根据键遍历查询请求结果，并返回键值
        :param res: 请求结果
        :param key: 结果中的某个键
        :return: 键值
        """
        if key in res.keys():
            return res[key]
        else:
            for k, v in res.items():
                v_type = type(v)
                while v_type != dict:
                    if v_type is list:
                        v_list = v
                        for vv in v_list:
                            if type(vv) is dict:
                                v = vv
                                v_type = dict
                    else:
                        break
                else:
                    if key in v.keys():
                        return v[key]

    def especcial_test(self):
        pass



if __name__ == '__main__':
    unittest.main()
